/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Header protection
#pragma once
#ifndef SKUGO_RTTI_HPP
#define SKUGO_RTTI_HPP

// Includes
#include "SkugoTypes.hpp"
#include <vector>

namespace Skugo
{
  // Using directives
  using std::vector;

  // Forward declarations
  class Buffer;
  struct TypeInfo;

  // This is basically a runtime pointer type that we use to point at objects
  struct RuntimePointer
  {
    // Default constructor
    RuntimePointer();

    // The object instance (what we are pointing to)
    const void* Instance;

    // The type of instance we are pointing to
    const TypeInfo* Type;
  };

  // The mode of serialization, provided for the below function
  namespace SerializeDirection
  {
    enum Enum
    {
      Reading,
      Writing
    };
  }

  // This function is used to both serialize and deserialize
  typedef void (*SerializeFn)(void* value, Buffer& buffer, SerializeDirection::Enum direction); 

  // This function is used to destroy an object
  typedef void (*DestructorFn)(void* self);

  // The wrapper function we use to change function pointers into a single signature
  typedef void (*InvokerFn)(void* self, RuntimePointer returns[], RuntimePointer parameters[]);

  // An attribute is basically something that gets attached to a member,
  // method, or type to describe some extra information (generally the user
  // derives their own meaning from their own attributes)
  struct Attribute
  {
    // Attributes need to be virtual and abstract
    virtual ~Attribute() = 0;
  };

  // A container for all attributes
  class AttributeContainer
  {
    // Add an attribute to the container (used for chaining)
    AttributeContainer& Add(Attribute* attribute);

    // All the custom attributes on the type/member/method
    vector<Attribute*> Attributes;
  };

  // All the information about data members of a class / struct
  struct MemberInfo : public AttributeContainer
  {
    // Default constructor
    MemberInfo();

    // The name of this member
    String Name;

    // A get function (or NULL if there is none)
    InvokerFn Get;

    // A set function (or NULL if there is none)
    InvokerFn Set;

    // The type of the member (required)
    const TypeInfo* Type;
  };

  // All the information about methods (static and instance)
  struct MethodInfo : public AttributeContainer
  {
    // Default constructor
    MethodInfo();
    
    // The name of this method
    String Name;

    // The wrapper function that we can use to invoke the method
    InvokerFn Function;

    // The type of the 'this' pointer in the method (NULL if it is a static method)
    const TypeInfo* ThisType;

    // The type of each parameter in the method
    vector<const TypeInfo*> ParameterTypes;

    // The type of all the returns in the method
    vector<const TypeInfo*> ReturnTypes;
  };

  // All the information about types (classes and structs)
  struct TypeInfo : public AttributeContainer
  {
    // Default constructor
    TypeInfo();

    // The name of this type
    String Name;

    // The guid allows us to uniquely identify and compare this type to another efficiently
    // One method to generate the guid is to hash the name of the type, and then
    // verify that the guid is globally unique to all other types
    // Another method is to use a global counter
    Guid TypeGuid;

    // The serializer allows reading/writing of this type to a buffer
    // This also indicates whether this class is a primitive or not (NULL if it's not primitive)
    SerializeFn PrimitiveSerializer;

    // The destructor tells us how to destroy an instance of this type
    // A NULL destructor implies that the object is a Pod and does not need to be destroyed
    DestructorFn Destructor;

    // The size of the type
    sizeType Size;

    // The parent base class (NULL if this is a true base class)
    const TypeInfo* Parent;

    // All the static methods on this type
    vector<MethodInfo> StaticMethods;

    // All the static data members on this type
    vector<MemberInfo> StaticMembers;

    // All the instance methods on this type
    vector<MethodInfo> InstanceMethods;

    // All the instance data members on this type
    vector<MemberInfo> InstanceMembers;
  };

  // Primitive type helpers
  struct Primitives
  {
    // Setup a string type (the serializer must be provided)
    // Strings must be serialized in the form of:
    // Size:    Little-endian uint64
    // Payload: Single byte for each character (no NULL terminator)
    static void SetupString(TypeInfo& stringType, SerializeFn serializer);

    // Setup a primitive type
    // It is not required to initialize primitives using this method, however, some frameworks
    // (such as Skugo) use them to identify primitive types, and therefore it is required
    template <typename T>
    static void Setup(TypeInfo& typeInfo);
  };

  // A helper macro that we use to declare all the setup specializations
  #define SkugoDeclarePrimitiveSetup(type)                    \
    template<>                                                \
    void Primitives::Setup<type>(TypeInfo& typeInfo);

  // Specializations for primitive type setup
  SkugoDeclarePrimitiveSetup(char);
  SkugoDeclarePrimitiveSetup(short);
  SkugoDeclarePrimitiveSetup(int);
  SkugoDeclarePrimitiveSetup(long);
  SkugoDeclarePrimitiveSetup(int64);
  SkugoDeclarePrimitiveSetup(uchar);
  SkugoDeclarePrimitiveSetup(ushort);
  SkugoDeclarePrimitiveSetup(uint);
  SkugoDeclarePrimitiveSetup(ulong);
  SkugoDeclarePrimitiveSetup(uint64);
  SkugoDeclarePrimitiveSetup(float);
  SkugoDeclarePrimitiveSetup(double);
  SkugoDeclarePrimitiveSetup(bool);
}

// End header protection
#endif